// sgu319
// 题意：
// 给定一个二维坐标坐标上，(0, 0)到(w, h)的矩形画布，现在要在里面画
// n(<=60000)个矩形（只画边框），任意两个矩形都没有交点，现在要求
// 所有在画布范围内的被矩形边框划分出一块块连续区域的面积，按不降序
// 输出这些面积。
//
// 题解：
// 扫描线+线段树。
// 因为任意两个矩形都没有交点，所以是一个树，我们按x从左往右扫描竖直线段，
// 遇到 +1 就求出它父亲的id，然后把它的id覆盖到对应这段区间，如果是-1
// 就把它父亲id覆盖回去。
//
// 然后就得到一棵树，dfs一遍就能得道答案。
//
// ml:run = $bin < input
// ml:ccf += -g
// ml:opt = 0
#include <iostream>
#include <vector>
#include <algorithm>

using ll = long long;
int const maxn = 60007;
int n;
int w, h;

struct seg { int pos, l, r, id, cover; };
bool operator<(seg const& a, seg const& b)
{
    return a.pos < b.pos || (a.pos == b.pos && a.cover > b.cover);
}

ll area[maxn];
seg segs[2 * maxn];
int dis[2 * maxn];
int father[maxn];
int tot, len;

int cover[8 * maxn];

std::vector<ll> ans;
std::vector<std::vector<int>> graph;

void push_down(int id)
{
    if (cover[id]) {
        cover[id * 2] = cover[id * 2 + 1] = cover[id];
        cover[id] = 0;
    }
}

void update(int id, int l, int r, int tl, int tr, int d)
{
    if (tl <= l && r <= tr) { cover[id] = d; return; }
    push_down(id);
    int mid = (l + r) / 2;
    if (tl <= mid) update(id * 2, l, mid, tl, tr, d);
    if (tr > mid) update(id * 2 + 1, mid + 1, r, tl, tr, d);
}

int query(int id, int l, int r, int x)
{
    if (cover[id]) return cover[id];
    push_down(id);
    int mid = (l + r) / 2;
    if (x <= mid) return query(id * 2, l, mid, x);
    else return query(id * 2 + 1, mid + 1, r, x);
}

void add_edge(int u, int v)
{
    graph[u].push_back(v);
    graph[v].push_back(u);
}

void dfs(int u, int f = -1)
{
    ll now = 0;
    for (auto v : graph[u]) {
        if (v == f) continue;
        now += area[v];
        dfs(v, u);
    }
    ans.push_back(area[u] - now);
}

int main()
{
    std::ios_base::sync_with_stdio(false);
    std::cin >> n >> w >> h;
    graph.resize(n + 4);
    dis[len++] = 0; dis[len++] = h;
    area[1] = (ll)w * (ll)h;
    for (int i = 2, x1, y1, x2, y2; i <= n + 1; i++) {
		std::cin >> x1 >> y1 >> x2 >> y2;
        int tx1 = std::min(x1, x2);
        int tx2 = std::max(x1, x2);
        int ty1 = std::min(y1, y2);
        int ty2 = std::max(y1, y2);

        area[i] = (ll)(tx2 - tx1) * (ll)(ty2 - ty1);

        segs[tot++] = {tx1, ty1, ty2, i, 1};
        segs[tot++] = {tx2, ty1, ty2, i, -1};

        dis[len++] = ty1; dis[len++] = ty2;
    }
    std::sort(dis, dis + len);
    len = std::unique(dis, dis + len) - dis;
    std::sort(segs, segs + tot);

    update(1, 1, len, 1, len, 1);
    for (int i = 0; i < tot; i++) {
        int l = std::lower_bound(dis, dis + len, segs[i].l) - dis + 1;
        int r = std::lower_bound(dis, dis + len, segs[i].r) - dis;
        if (segs[i].cover > 0) {
            father[segs[i].id] = query(1, 1, len, l);
            add_edge(segs[i].id, father[segs[i].id]);
            update(1, 1, len, l, r, segs[i].id);
        } else {
            update(1, 1, len, l, r, father[segs[i].id]);
        }
    }

    dfs(1);
    std::sort(ans.begin(), ans.end());
    for (auto i : ans) std::cout << i << " ";
    std::cout << "\n";
}

